package org.zxt.web.nio;

import lombok.extern.slf4j.Slf4j;
import sun.nio.ch.sctp.SctpStdSocketOption;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.logging.Level;

@Slf4j
public class ServerRunnable implements Runnable {

    private final int timeout;

    private ServerSocketChannel myServerSocket;

    private boolean hasBinded;
    NioHTTPD server;
    Selector selector;

    public ServerRunnable(NioHTTPD server, ServerSocketChannel myServerSocket, Selector selector, int timeout) {
        this.timeout = timeout;
        this.myServerSocket = myServerSocket;
        this.server = server;
        this.selector = selector;
    }

    @Override
    public void run() {
        try {
            myServerSocket.bind(this.server.hostname != null ? new InetSocketAddress(this.server.hostname, this.server.myPort) : new InetSocketAddress(this.server.myPort));
            myServerSocket.register(selector, SelectionKey.OP_ACCEPT);
            hasBinded = true;
            do {
                selector.select();
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while (it.hasNext()) {
                    try {
                        SelectionKey selectionKey = it.next();
                        it.remove();
                        if (selectionKey.isAcceptable()) {
                            SocketChannel sc = myServerSocket.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
//                            InetAddress address = sc.socket().getInetAddress();
//                            log.debug("connect from ip {} {}", address.getHostAddress(), address.getHostName());
                        } else if (selectionKey.isReadable()) {
                            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                            if (!socketChannel.socket().isClosed() && !this.server.callbackHandlerMap.containsKey(socketChannel)) {
                                TempFileManager tempFileManager = this.server.tempFileManagerFactory.create();
                                HTTPSession session = new HTTPSession(this.server, tempFileManager, socketChannel.socket().getInputStream(), socketChannel, selectionKey);
                                int len = session.read();
                                if(len > 0){
                                    this.server.callbackHandlerMap.put(socketChannel, session);
                                    this.server.asyncRunner.exec(session);
                                } else {
                                    selectionKey.cancel();
                                }
                            }
                        } else if(selectionKey.isWritable()){
                            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                            socketChannel.register(selector, SelectionKey.OP_READ);
                        }
                    } catch (CancelledKeyException e){
                        e.printStackTrace();
                    }
                }
            } while (this.myServerSocket.isOpen());
        } catch (ClosedSelectorException e) {
            System.out.println("nio httpd will be shutdown");
        } catch (IOException e) {
            NioHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
        }
    }
}
